Crate takecell

Source
Expand description

This crate provides two new cell-like types, TakeCell and TakeOwnCell. Both may store arbitrary non-Copy types, can be read from at most once and provide direct unique access to the stored contents. The core API looks roughly like this (and there’s much more inside, read on!):

impl<T> TakeCell<T> {
    const fn new(v: T) -> Self { ... }
}
impl<T: ?Sized> TakeCell<T> {
    fn take(&self) -> Option<&mut T> { ... }
}

impl<T> TakeOwnCell<T> {
    const fn new(v: T) -> Self { ... }
    fn take(&self) -> Option<T> { ... }
}

Note that, like with RefCell and Mutex, the take method requires only a shared reference. Because of the single read restriction take can return a &mut T or T instead of RefMut<T> or MutexGuard<T>. In some sense TakeCell can be thought as a Mutex without unlocking (or rather with unlocking requiring unique access to the Mutex, see heal).

This crate is #![no_std] and only requires little sychronization via 8-bit atomic.

§Usage examples

§Singletons

TakeCell is Sync (when T: Sync) and as such it may be used in statics. This can be used to create singletons:

use takecell::TakeCell;

#[non_exhaustive]
pub struct Peripherals {
    pub something: Something,
}

pub static PEREPHERALS: TakeCell<Peripherals> = TakeCell::new(Peripherals {
    something: Something,
});

let peripherals: &'static mut _ = PEREPHERALS.take().unwrap();

§Doing work only once

use once_cell::sync::OnceCell;
use std::sync::{Arc, Condvar, Mutex};
use takecell::TakeCell;

#[derive(Clone)]
struct Job {
    // Input can be a type which requires unique access to be used (e.g.: `dyn Read`)
    input: Arc<TakeCell<Input>>,
    output: Arc<OnceCell<Output>>,
    wait: Arc<(Mutex<bool>, Condvar)>,
}

fn execute(job: Job) -> Output {
    match job.input.take() {
        Some(input) => {
            // Nobody has started executing the job yet, so execute it
            let output = input.process();

            // Write the output
            job.output.set(output);

            // Notify other threads that the job is done
            let (lock, cvar) = &*job.wait;
            let mut done = lock.lock().unwrap();
            *done = true;
        }
        None => {
            // Wait for the other thread to do the job
            let (lock, cvar) = &*job.wait;
            let mut done = lock.lock().unwrap();
            // As long as the value inside the `Mutex<bool>` is `false`, we wait
            while !*done {
                done = cvar.wait(done).unwrap();
            }
        }
    }

    // Read the output
    job.output.get().unwrap().clone()
}

impl Input {
    fn process(&mut self) -> Output {
        // ...
    }
}

Structs§

  • A cell type which value can be taken only once.
  • A cell type which value can be taken only once.